
package trim.testalgorithms.optresttrim;

import java.util.ArrayList;
import java.util.List;
import trim.common.Format;
import trim.common.Material;
import trim.common.inputdata.FormatOrder;
import trim.common.inputdata.MaterialStock;
import trim.differentmaterials.coefficients.Coefficients;
import trim.differentmaterials.generator.columns.Column;
import trim.differentmaterials.generator.columns.RealPattern;

/**
 *
 * @author Podorov Alexander
 */

public class SecondTrimmer
{
    private List<MaterialStock[]> materialStockList;
    private FormatOrder[] formatOrder;
    private MaterialStock[] materialStock;
    private int formatCount;
    private int materialCount;
    private List<Column> patterns;
    private Coefficients coefficients;

    public SecondTrimmer(List<MaterialStock[]> materialStockList, FormatOrder[] formatOrder, MaterialStock[] materialStock, List<Column> patterns, Coefficients coefficients)
    {
        this.materialStockList = materialStockList;
        this.formatOrder = formatOrder;
        this.materialStock = materialStock;
        formatCount = formatOrder.length;
        materialCount = materialStock.length;
        this.patterns = patterns;
        this.coefficients = coefficients;
    }

    private class FormatCountIndex
    {
        private Format format;
        private int count;
        private int index;

        public FormatCountIndex(Format format, int count, int index)
        {
            this.format = format;
            this.count = count;
            this.index = index;
        }

        public Format getFormat()
        {
            return format;
        }

        public int getCount()
        {
            return count;
        }

        public int getIndex()
        {
            return index;
        }

        public void decreaseCount()
        {
            count--;
        }
    }

    private class Heap
    {
        private Material material;
        private List<FormatCountIndex> items;
        private int rest;

        public Heap(Material material)
        {
            this.material = material;
            items = new ArrayList<FormatCountIndex>();
            rest = material.getLength();
        }

        public Material getMaterial()
        {
            return material;
        }

        public List<FormatCountIndex> getItems()
        {
            return items;
        }

        public int getRest()
        {
            return rest;
        }

        public int getSize()
        {
            return items.size();
        }

        public void put(Format format, int count, int index)
        {
            rest -= format.getLength() * count;
            items.add(new FormatCountIndex(format, count, index));
        }

        public int popIndex()
        {
            FormatCountIndex item = items.get(items.size() - 1);
            if (item.getCount() == 1)
            {
                items.remove(items.size() - 1);
            }
            else
            {
                item.decreaseCount();
            }
            rest += item.getFormat().getLength();
            return item.getIndex();
        }

        public void clearHeap(FormatOrder[] formatOrderRest)
        {
            for (FormatCountIndex item : items)
            {
                formatOrderRest[item.getIndex()].setCount(formatOrderRest[item.getIndex()].getCount() + item.getCount());
            }
            items.clear();
            rest = material.getLength();
        }
    }

    private int lengthToNumber(int length)
    {
        for (int i = 0; i < formatCount; i++)
        {
            if (formatOrder[i].getFormat().getLength() == length)
            {
                return i;
            }
        }
        return - 1;
    }

    public void process()
    {
        int totalFormatLength = 0;
        for (int i = 0; i < formatCount; i++)
        {            
            totalFormatLength += formatOrder[i].getFormat().getLength() * formatOrder[i].getCount();
        }

        int currentCombination = 0;

        List<Heap> heaps = new ArrayList<Heap>();
        int maxRest = 0;
        int tempRest = 0;
        for (MaterialStock currentMaterialStock : materialStockList.get(currentCombination))
        {
            heaps.add(new Heap(currentMaterialStock.getMaterial()));
            maxRest += currentMaterialStock.getMaterial().getLength() * currentMaterialStock.getCount();
        }
        maxRest -= totalFormatLength;        

        int currentHeap = 0;        

        int last = - 1;
        int currentFormat = 0;

        while (currentHeap < heaps.size())
        {
            for (int i = currentFormat; i < formatCount; i++)
            {
                int count = Math.min(heaps.get(currentHeap).getRest() / formatOrder[i].getFormat().getLength(), formatOrder[i].getCount());
                if (count > 0)
                {
                    heaps.get(currentHeap).put(formatOrder[i].getFormat(), count, i);
                    formatOrder[i].setCount(formatOrder[i].getCount() - count);
                }
            }

            if (heaps.get(currentHeap).getSize() > 0)
            {
                if ((tempRest + heaps.get(currentHeap).getRest() <= maxRest) )
                {
                    tempRest += heaps.get(currentHeap).getRest();
                    currentHeap++;
                    currentFormat = 0;
                }
                else
                {
                    last = heaps.get(currentHeap).popIndex();
                    formatOrder[last].setCount(formatOrder[last].getCount() + 1);
                    currentFormat = last + 1;
                }
            }
            else
            {                
                if (currentHeap == 0)
                {
                    currentHeap = 0;
                    currentFormat = 0;

                    heaps.clear();

                    currentCombination++;
                    maxRest = 0;
                    MaterialStock[] currentMaterialStock = materialStockList.get(currentCombination);
                    for (int h = 0; h < currentMaterialStock.length; h++)
                    {
                        for (int u =0; u < currentMaterialStock[h].getCount(); u++)
                        {
                            heaps.add(new Heap(currentMaterialStock[h].getMaterial()));
                        }
                        maxRest += currentMaterialStock[h].getMaterial().getLength() * currentMaterialStock[h].getCount();
                    }
                    maxRest -= totalFormatLength;
                }
                else
                {
                    currentHeap--;
                    tempRest -= heaps.get(currentHeap).getRest();
                    last = heaps.get(currentHeap).popIndex();
                    formatOrder[last].setCount(formatOrder[last].getCount() + 1);
                    currentFormat = last + 1;
                }
            }
        }

        for (int i = 0; i < heaps.size(); i++)
        {
            int[] items = new int[formatCount];
            for (FormatCountIndex item : heaps.get(i).getItems())
            {
                items[lengthToNumber(item.getFormat().getLength())] = item.getCount();
            }
            RealPattern pattern = new RealPattern(items, Double.MAX_VALUE, coefficients.getCoefficient(RealPattern.class), heaps.get(i).getMaterial());
            pattern.setCount(1);
            patterns.add(pattern);
        }
    }
}
